#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <sys/select.h>
#include "log.hpp"
#include "Sock.hpp"

using std::string;
using std::cout;
using std::endl;

#define BITS 8
#define NUM sizeof(BITS * sizeof(fd_set))   // 这里sizeof fd_set是计算位图的大小, 一个字节是8个bit位, 相乘就是select的最大极限
#define FD_NOME -1

class SelectServer
{
public:
    SelectServer(const uint16_t& port = 8080)
        :_port(port)
    {
        _listensock = Sock::Socket();
        Sock::Bind(_listensock, _port);
        Sock::Listen(_listensock);

        // 先初始化第三方数组,然后把_listensock放入第三方数组
        for (int i = 0; i < NUM; i++)
        {
            _fd_array[i] = FD_NOME;  // 初始化
        }

        _fd_array[0] = _listensock; // 放进去
    }

    void start()
    {
        while (true)
        {
            // 先打印一下数组中的文件描述符
            DebugPrint();

            // 先定义 fd_set
            fd_set rfds;
            FD_ZERO(&rfds);  // 清零

            int maxfd = 0;  // select的第一个参数
            for (int i = 0; i < NUM; i++)
            {
                if (_fd_array[i] == FD_NOME)    continue;
                FD_SET(_fd_array[i], &rfds);
                if (_fd_array[i] > maxfd)   maxfd = _fd_array[i];  // 如果你大,那我就更新maxfd,因为文件描述符是按顺序开辟的
            }

            int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr); // 这里阻塞式读
            
            switch(n)
            {
            case 0:
                logMessage(NORMAL, "%s", "time out");
                break;
            case -1:
                logMessage(ERROR, "select error, %d: %s", errno, strerror(errno));
                break;
            default:  // 有数据就绪,执行Handler方法
                logMessage(NORMAL, "get a new link...");
                Handler(rfds);  // fd_set需要传递, 方便我查找那个sock就绪了
                break;
            }
        }
    }

    ~SelectServer()
    {
        if (_listensock >= 0) close(_listensock);
    }
private:
    void DebugPrint()
    {
        cout << "_fd_array[]: ";
        for (int i = 0; i < NUM; i++)
        {
            if (_fd_array[i] != FD_NOME)
                cout << _fd_array[i] << " ";
        }
        cout << endl;
    }

    void Handler(const fd_set& rfds)
    {
        for (int i = 0; i < NUM; i++)
        {
            // 1.处理合理的
            if (_fd_array[i] == FD_NOME)    continue;

            if (FD_ISSET(_fd_array[i], &rfds))
            {
                if (_fd_array[i] == _listensock)
                {
                    // 连接
                    Accept();
                }
                else
                {
                    // 读取
                    Recv(i);
                }
            }
        }
    }

    void Accept()
    {
        uint16_t client_port;
        string client_ip;

        int sock = Sock::Accpet(_listensock, &client_port, &client_ip);
        if (sock < 0)
        {
            logMessage(ERROR, "accept error, %d: %s", errno, strerror(errno));
            return ;
        }
        logMessage(DEBUG, "accept error");

        // 然后把连接好的 sock 放入数组中
        int pos = 1;
        for (; pos < NUM; pos++)
        {
            if (_fd_array[pos] == FD_NOME) break;
        }
        if (pos == NUM) // 证明数组满了
        {
            logMessage(WARNING, "%s: %d", "select server alreadly full, close:", sock);
            close(sock);
        }
        else
        {
            _fd_array[pos] = sock;
        }
    }

    void Recv(int pos)
    {
        logMessage(NORMAL, "message in Get IO event: %d", _fd_array[pos]);

        char buffer[1024];
        int n = recv(_fd_array[pos], buffer, sizeof(buffer)-1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            logMessage(NORMAL, "client[%d]# %s", _fd_array[pos], buffer);
        }
        else if (n == 0)
        {
            logMessage(NORMAL, "client[%d] quit, me too..", _fd_array[pos]);
            close(_fd_array[pos]);
            _fd_array[pos] = FD_NOME;
        }
        else
        {
            logMessage(ERROR, "%d sock recv error, %d: %s", _fd_array[pos], errno, strerror(errno));
            close(_fd_array[pos]);
            _fd_array[pos] = FD_NOME;
        }
    }
private:
    int _listensock;
    uint16_t _port;
    int _fd_array[NUM];  // 第三方数组 -> 读事件   这里只实现读事件
    int _fd_write[NUM];  // 第三方数组 -> 写事件
    int _fd_except[NUM]; // 第三方数组 -> 异常事件
};
